12318. Сумма в поле

 

Для заданных целых чисел a, b, c, n, p вычислите значение суммы:

Результат выведите в виде двух чисел x и y таких что

Вход. Пять целых чисел a, b, c, n, p. Известно, что:

·        p < 109простое число,

·        0a, b < p,

·        1 ≤ c < p,

·        0n ≤ 109

 

Выход. Выведите два целых числа x и y – коэффициенты при 1 и  соответственно, по модулю p.

 

Пример входа 1

Пример выхода 1

2 1 5 2 17

12 5

 

 

Пример входа 2

Пример выхода 2

3 2 5 10 17

15 6

 

 

РЕШЕНИЕ

комбинаторика

 

Анализ алгоритма

Вычисления следует выполнять в расширенном поле:

Правила операций:

·        Сложение

·        Умножение

 

Пусть x = . Тогда искомая сумма является конечной геометрической прогрессией:

1 + x + x2 + x3 + … + xn =  =

Остается реализовать деление a = xn+1 – 1 на b = x – 1 в поле :

=  =  =  =

 

Пример

В первом примере

 =

(1 + () + ()) mod 17 =  12

Вычислим этот пример по формуле:

 =  =

=  =  =  =

 =  =  =  =

 =  =

 

Во втором примере

 = 15

 

Реализация алгоритма

Функция modpow вычисляет значение ak mod p.

 

long long modpow(long long a, long long k, long long p)

{

  long long res = 1;

  a %= p;

  while (k)

  {

    if (k & 1) res = res * a % p;

    a = a * a % p;

    k >>= 1;

  }

  return res;

}

 

Объявим структуру Num для хранения числа вида .

 

struct Num

{

  long long x, y; // x + y*sqrt(c)

};

 

Реализуем сложение в поле . Функция add возвращает сумму чисел a и b.

 

Num add(Num& a, Num& b)

{

  Num res;

  res.x = (a.x + b.x) % p;

  res.y = (a.y + b.y) % p;

  return res;

}

 

Реализуем вычитание в поле . Функция sub возвращает разность чисел a и b.

 

Num sub(Num& a, Num& b)

{

  Num res;

  res.x = (a.x - b.x + p) % p;

  res.y = (a.y - b.y + p) % p;

  return res;

}

 

Реализуем умножение в поле . Функция mult возвращает произведение чисел a и b.

 

Num mult(Num& a, Num& b)

{

  Num res;

  res.x = (a.x * b.x + c * a.y % p * b.y) % p;

  res.y = (a.x * b.y + a.y * b.x) % p;

  return res;

}

 

Функция pow реализует возведение в степень basen, где base – число в поле .

 

Num pow(Num base, long long n)

{

  Num res = { 1, 0 }; // единица поля

  while (n > 0)

  {

    if (n & 1)

      res = mult(res, base);

    base = mult(base, base);

    n >>= 1;

  }

  return res;

}

 

Функция divide реализует деление в поле . Она возвращает частное чисел a и b. Деление реализуем через умножение на сопряженное:

=  =  =  =

 

Num divide(Num& a, Num& b)

{

 

Пусть conj = .

 

  Num conj = { b.x, (p - b.y) % p };

 

Вычисляем n = .

 

  Num n = mult(a, conj);

 

Вычисляем denom = .

 

  long long denom =

    ((b.x * b.x) % p - ((b.y * b.y) % p * c) % p + p) % p;

 

Вычисляем обратный элемент и возвращаем n / denom = n * denom-1.

 

  long long inv_denom = modpow(denom, p - 2, p); // обратный элемент

  return Num((n.x * inv_denom) % p, (n.y * inv_denom) % p);

}

 

Основная часть программы. Читаем входные данные.

 

scanf("%lld %lld %lld %lld %lld", &a, &b, &c, &n, &p);

 

Инициализируем число x =  и единицу поля one = .

 

Num x = { a % p, b % p };

Num one = { 1, 0 };

 

Вычисляем num = xn+1 – 1, denom = x – 1.

 

Num num = pow(x, n + 1);

num = sub(num, one);

Num denom = sub(x, one);

 

Вычисляем и выводим ответ res = num / denom = .

 

Num res = divide(num, denom);

printf("%lld %lld\n", res.x, res.y);